import {ChatAdapterExtras} from './chatAdapterExtras';

/**
 * This type is used to indicate the mode in which the adapter should request data from the API.
 */
export type DataTransferMode = 'stream' | 'batch';

/**
 * The type for the function used to submit a message to the API in stream mode.
 *
 * @param {string} message
 * @param {StreamingAdapterObserver} observer
 * @param {ChatAdapterExtras} extras
 */
export type StreamSend<AiMsg = string> = (
    message: string,
    observer: StreamingAdapterObserver<AiMsg>,
    extras: ChatAdapterExtras<AiMsg>,
) => void;

/**
 * The type for the function used to submit a message to the API in batch mode.
 * It should return a promise that resolves to the response from the API.
 *
 * @param `string` message
 * @param `ChatAdapterExtras` extras
 * @returns Promise<string>
 */
export type BatchSend<AiMsg = string> = (
    message: string,
    extras: ChatAdapterExtras<AiMsg>,
) => Promise<AiMsg>;

/**
 * This interface exposes methods that should be implemented by any chat adapter to connect the AiChat component
 * to any API or AI backend. Chat adapters can be used to request data from the API in batch mode or stream mode.
 *
 * The difference between this and the `AssistAdapter` interface is that this adapter can only return a text response
 * to be displayed to the user. It cannot return a task to be executed by the client. If you are using the `AiChat`
 * component in co-pilot mode, you should use the `AssistAdapter` interface instead.
 */
export interface ChatAdapter<AiMsg = string> {
    /**
     * This method should be implemented by any adapter that wants to request data from the API in batch mode.
     * It should return a promise that resolves to the response from the API.
     * Either this method or `streamText` (or both) should be implemented by any adapter.
     *
     * @param `string` message
     * @param `ChatAdapterExtras` extras
     * @returns Promise<string>
     */
    batchText?: BatchSend<AiMsg>;

    /**
     * This method should be implemented by any adapter to be used with nlux.
     * Either this method or `batchText` (or both) should be implemented by any adapter.
     *
     * @param {string} message
     * @param {StreamingAdapterObserver} observer
     * @param {ChatAdapterExtras} extras
     */
    streamText?: StreamSend<AiMsg>;
}

/**
 * This interface is used to capture the stream of data being generated by the API and send it to the AiChat
 * user interface as it's being generated.
 */
export interface StreamingAdapterObserver<ChunkType = string> {
    /**
     * This method should be called by the adapter when it has completed sending data to the AiChat user interface.
     * This will result in the AiChat component removing the loading indicator and resetting the conversation
     * text input.
     */
    complete(): void;

    /**
     * This method should be called by the adapter when it has an error to send to the AiChat user interface.
     * This will result in the AiChat component displaying an error message to the user, resetting the
     * conversation text input, removing the loading indicator, removing the message sent from the conversation.
     *
     * The error will be logged to the console, but it will not be displayed to the user. A generic error message
     * will be displayed to the user instead.
     *
     * @param {Error} error
     */
    error(
        error: Error,
    ): void;

    /**
     * This method should be called by the adapter when it has new data to send to the AiChat user interface.
     * @param {ChunkType} chunk being sent as part of the stream.
     */
    next(
        chunk: ChunkType,
    ): void;
}
